package com.example.demo.leetcode;

import java.util.HashSet;

/**
 * 链接：@see <a href="https://leetcode.cn/problems/linked-list-cycle/?envType=study-plan-v2&envId=top-interview-150">https://leetcode.cn/problems/linked-list-cycle/?envType=study-plan-v2&envId=top-interview-150</a>
 * <br>
 * 给你一个链表的头节点 head ，判断链表中是否有环。
 * <p>
 * 如果链表中有某个节点，可以通过连续跟踪 next 指针再次到达，则链表中存在环。
 * <p>
 * 如果链表中存在环 ，则返回 true 。 否则，返回 false 。
 */
public class _141_环形链表 {

    public static void main(String[] args) {


    }


    public static class Solution {
        /**
         * 方法一: 哈希表<br>
         * 具体地，我们可以使用哈希表来存储所有已经访问过的节点。<br>
         * 每次我们到达一个节点，如果该节点已经存在于哈希表中，则说明该链表是环形链表，否则就将该节点加入哈希表中。<br>
         * 重复这一过程，直到我们遍历完整个链表即可。<br>
         * <p>
         * <p>
         * 时间复杂度为O(n) 其中 N 是链表中的节点数。最坏情况下我们需要遍历每个节点一次。<br>
         * <p>
         * 空间复杂服为O(n) 其中 N 是链表中的节点数。主要为哈希表的开销，最坏情况下我们需要将每个节点插入到哈希表中一次。<br>
         */
        public boolean hasCycle(ListNode head) {
            HashSet<ListNode> seen = new HashSet<>();
            while (head != null) {

                /**
                 * 如果指定元素不存在，则将该元素添加到此集合。并返回true。
                 * 如果该集合已经包含该元素，则保持该集合不变。并返回false。
                 */
                if (!seen.add(head)) {
                    return true;
                }
                head = head.next;
            }
            return false;
        }


        /**
         * 方法二：快慢指针<br>
         * 本方法需要读者对「Floyd 判圈算法」（又称龟兔赛跑算法）有所了解。<br>
         * <p>
         * 假想「乌龟」和「兔子」在链表上移动，「兔子」跑得快，「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时，如果该链表中没有环，那么「兔子」将一直处于「乌龟」的前方；如果该链表中有环，那么「兔子」会先于「乌龟」进入环，并且一直在环内移动。等到「乌龟」进入环时，由于「兔子」的速度快，它一定会在某个时刻与乌龟相遇，即套了「乌龟」若干圈。
         * </p>
         * <br>
         * <p>
         * 我们可以根据上述思路来解决本题。具体地，我们定义两个指针，一快一慢。慢指针每次只移动一步，而快指针每次移动两步。初始时，慢指针在位置 head，而快指针在位置 head.next。这样一来，如果在移动的过程中，快指针反过来追上慢指针，就说明该链表为环形链表。否则快指针将到达链表尾部，该链表不为环形链表。
         * </p>
         * <br>
         * <p>
         * 时间复杂度：O(N)，其中 N 是链表中的节点数。
         * 当链表中不存在环时，快指针将先于慢指针到达链表尾部，链表中每个节点至多被访问两次。
         * 当链表中存在环时，每一轮移动后，快慢指针的距离将减小一。而初始距离为环的长度，因此至多移动 N 轮。
         *
         * <p>
         * 空间复杂度：O(1)。我们只使用了两个指针的额外空间。
         */
        public boolean hasCycle2(ListNode head) {
            ListNode slow = head, fast = head; // 乌龟和兔子同时从起点出发
            while (fast != null && fast.next != null) {
                slow = slow.next; // 乌龟走一步
                fast = fast.next.next; // 兔子走两步
                if (fast == slow) { // 兔子追上乌龟（套圈），说明有环
                    return true;
                }
            }
            return false; // 访问到了链表末尾，无环
        }

    }

    /**
     * Definition for singly-linked list.
     */
    static class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }

}
